#include "networkmanage.h"

#include "MsgParser.h"
#include "mylogger.h"
#include "msgparserc.h"
#include "machineutil.h"

using namespace ns_yht;

NetworkManage *NetworkManage::g_instance = nullptr;

NetworkManage::NetworkManage(QObject *parent) : PortManage(parent)
{
}

NetworkManage::~NetworkManage()
{
    if (m_udpSocket)
    {
        if (isUdpOpened())
        {
            closeUdpSocket();
        }
        m_udpSocket->deleteLater();
    }
    if (m_tcpSocket)
    {
        if (isTcpOpened())
        {
            closeTcpSocket();
        }
        m_tcpSocket->deleteLater();
    }
    g_instance = nullptr;
}

NetworkManage *NetworkManage::instance()
{
    if (!g_instance)
        g_instance = new NetworkManage();
    return g_instance;
}

QUdpSocket *NetworkManage::getUdpSocket() const
{
    return m_udpSocket;
}

QTcpSocket *NetworkManage::getTcpSocket() const
{
    return m_tcpSocket;
}

bool NetworkManage::isTcpOpened() const
{
    return m_tcpSocket && (m_tcpSocket->state() == QAbstractSocket::ConnectedState);
}

bool NetworkManage::isUdpOpened() const
{
    return m_udpSocket && (m_udpSocket->state() == QAbstractSocket::BoundState);
}

void NetworkManage::openTcpSocket(const QString &server, int port)
{
    if (server.isEmpty() || port <= 0)
        return;
    if (m_tcpSocket)
    {
        if (m_tcpSocket->isOpen())
        {
            if (server == m_tcpServer && port == m_tcpPort && (m_tcpSocket->state() == QAbstractSocket::ConnectingState || m_tcpSocket->state() == QAbstractSocket::ConnectedState))
            {
                return;
            }
        }
    }
    else
    {
        m_tcpSocket = new QTcpSocket;
        connect(m_tcpSocket, &QAbstractSocket::readyRead, this, &NetworkManage::onReadyRead_tcp);
    }
    m_tcpServer = server;
    m_tcpPort = port;
    m_tcpSocket->abort();
    m_tcpSocket->connectToHost(server, port, QIODevice::ReadOnly, QAbstractSocket::IPv4Protocol);

    if (m_tcpSocket->waitForConnected(3000))
    {
        if (GLOG)
            GLOG->debug("tcp connectToHost succeed! --- {} {}", server.toStdString(), port);
        emit tcpChanged(true);
    }
    else
    {
        if (GLOG)
            GLOG->debug("tcp connectToHost failed! --- {} {}", server.toStdString(), port);
        emit tcpChanged(false);
    }
}

void NetworkManage::checkTcpSocket()
{
    if (m_tcpSocket && (m_tcpSocket->state() == QAbstractSocket::ConnectingState || m_tcpSocket->state() == QAbstractSocket::ConnectedState))
        return;
}

void NetworkManage::openUdpSocket(int port)
{
    if (GLOG)
        GLOG->debug("openUdpSocket----------------------------------{}", port);
    if (m_udpSocket)
    {
        if (isUdpOpened())
        {
            return;
        }
    }
    else
    {
        m_udpSocket = new QUdpSocket;
        connect(m_udpSocket, &QAbstractSocket::readyRead, this, &NetworkManage::onReadyRead_udp);
    }

    m_udpSocket->abort();
    bool result = m_udpSocket->bind(QHostAddress::AnyIPv4, port);

    if (result)
    {
        if (GLOG)
            GLOG->debug("udp bind succeed! ---  {}", port);
        emit udpChanged(true);
    }
    else
    {
        if (GLOG)
            GLOG->debug("udp bind failed! ---  {}", port);
    }
}

void NetworkManage::closeUdpSocket()
{
    if (m_udpSocket)
    {
        m_udpSocket->abort();
        m_udpSocket->close();
        emit udpChanged(false);
    }
}

void NetworkManage::closeTcpSocket()
{
    if (m_tcpSocket)
    {
        m_tcpSocket->abort();
        m_tcpSocket->disconnectFromHost();
        m_tcpSocket->close();
        emit tcpChanged(false);
    }
}

void NetworkManage::onReadyRead_tcp()
{
    if (isTcpOpened() && m_tcpSocket->bytesAvailable())
    {
        m_buffer = m_tcpSocket->readAll();
        m_msgQueue.enqueue(m_buffer);
        if (GLOG_AIS)
            GLOG_AIS->info(m_buffer);
    }
}

void NetworkManage::onReadyRead_udp()
{
    if (isUdpOpened() && m_udpSocket->hasPendingDatagrams())
    {
        m_buffer.resize(m_udpSocket->pendingDatagramSize());
        m_udpSocket->readDatagram(m_buffer.data(), m_buffer.size());
        m_msgQueue.enqueue(m_buffer);

        if (GLOG_AIS)
            GLOG_AIS->info(m_buffer);
    }
}

QString NetworkManage::getPortsSetting() const
{
    QString s;
    const QChar sep(0x1E);

    if (isTcpOpened())
        s = s.append(QString("%1|%2").arg(m_tcpSocket->peerAddress().toString()).arg(m_tcpSocket->peerPort()));
    else
        s = s.append(QString("|"));

    s.append(sep);

    if (isUdpOpened())
        s = s.append(QString("%1").arg(m_udpSocket->localPort()));
    else
        s.append(QString("0"));

    return s;
}

void NetworkManage::setPortsSetting(const QString &portsSetting)
{
    const QChar sep(0x1E);
    QVector<QStringRef> ss = portsSetting.splitRef(sep);
    if (ss.count() > 0)
    {
        QStringRef s1 = ss.first();
        QVector<QStringRef> s1s = s1.split('|');
        if (s1s.count() == 2)
        {
            QString server = s1s.first().toString();
            int remotePort = s1s.last().toInt();
            openTcpSocket(server, remotePort);
        }
    }
    if (ss.count() > 1)
    {
        QStringRef s2 = ss.last();
        int localPort = s2.toInt();
        if (localPort > 0)
            openUdpSocket(localPort);
    }
}

void NetworkManage::moveToThreadEx(QThread *thread)
{
    PortManage::moveToThreadEx(thread);

    if (m_tcpSocket)
        m_tcpSocket->moveToThread(thread);
    if (m_udpSocket)
        m_udpSocket->moveToThread(thread);
}
